#include "TcpConnection.h"
#include "Logger.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"

static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
    if (loop == nullptr)
    {
        LOG_FATAL("%s:%s:%d: baseloop is null", __FILE__, __FUNCTION__, __LINE__);
    }
    return loop;
}

TcpConnection::TcpConnection(EventLoop *loop,
                             const std::string &nameArg,
                             int sockfd,
                             const InetAddress &localAddr,
                             const InetAddress &peerAddr)
    : loop_(CheckLoopNotNull(loop))
    , name_(nameArg)
    , state_(kConnecting)
    , reading_(true)
    , socket_(new Socket(sockfd))
    , channel_(new Channel(loop, sockfd))
    , localAddr_(localAddr)
    , peerAddr_(peerAddr)
    , highWaterMark_(64 * 1024 * 1024) // 64M
    , enableInactiveRelease_(false)
{
    // 给channel设置 对应事件的回调函数
    channel_->setReadCallback(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
    channel_->setWriteCallbcak(std::bind(&TcpConnection::handleWrite, this));
    channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
    channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));
    channel_->setrEventCallback(std::bind(&TcpConnection::refresh,this));

    LOG_INFO("TcpConnection-%p : [%s] at fd=%d", loop_, name_.c_str(), sockfd);
    socket_->setKeepAlive(true); // 启动保活机制
}

TcpConnection::~TcpConnection()
{
    LOG_INFO("TcpConnection::~ [%s] at fd=%d state=%s", name_.c_str(), channel_->fd(), stateToString());
    //telnet连接测试shutdown时，会调用handleclose，可以打印这一句
    // 用客户端连接测试时，shutdown只是关闭写端，不会关闭连接，这句不会打印，除非连接断开
}

// 处理完事件后，刷新非活跃消毁任务
void TcpConnection::refresh()
{
    loop_->timerRefresh(name_);
}

void TcpConnection::send(const std::string &message)
{
    if (state_ == kConnected)
    {
        // if (loop_->isInLoopThread())
        // {
        //     sendInLoop(message.c_str(), message.size());
        // }
        // else
        // {
        //     loop_->runInLoop(std::bind(&TcpConnection::sendInLoop,
        //                                this, message.c_str(),
        //                                message.size()));
        // }

        // 直接调runInLoop     改动
         loop_->runInLoop(std::bind(&TcpConnection::sendInLoop,
                                 this,message.c_str(),
                                 message.size()));
    }
}

/**
 * 发送数据，先直接给fd发，如果发送完了，不需要设置EPOLLOUT。
 * 如果没发完，就把数据放入应用层的outbuffer，并设置对fd的写事件关心。
 * 构造函数注册了fd的写事件处理函数hanldeWrite。当写事件就绪时，
 * cahnnel调用writeCallback，也就是上面注册的函数，通过writeFd 自动将数据发送
 */
void TcpConnection::sendInLoop(const void *message, size_t len)
{
    ssize_t nwrote = 0;      // 发送完的数据
    size_t remaining = len;  // 没发送完的数据
    bool faultError = false; // 标识是否出错

    if (state_ == kDisconnected)
    {
        LOG_ERROR("disconnected, give up writing!");
        return;
    }

    if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
    {
        nwrote = write(channel_->fd(), message, len);
        if (nwrote >= 0)
        {
            remaining = len - nwrote;
            if (remaining == 0 && writeCompleteCallback_) // 数据发送完了，不需要设置写关心了
            {
                loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
            }
        }
        else
        {
            nwrote = 0;
            // 非阻塞fd中，EWOULDBLOCK对于写，是TCP发送缓冲区满了，不算出错。其他情况需要打印错误
            // EAGAIN和EINTR是不是也不算错误？  TO PONDER
            //  if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
            if (errno != EWOULDBLOCK)
            {
                LOG_ERROR("TcpConnection::sendInLoop");
                if (errno == EPIPE || errno == ECONNRESET) // 客户端异常退出
                {
                    faultError = true;
                }
            }
        }
    }

    if (!faultError && remaining > 0)
    {
        size_t oldLen = outputBuffer_.readableBytes();
        if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ // 表明上次没有调用 高水位函数
            && highWaterMarkCallback_)
        {
            loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
        }

        // 把没发完的数据放入outbuffer，并设置对fd的cahnnel的写事件关心
        outputBuffer_.append(static_cast<const char *>(message) + nwrote, remaining);
        if (!channel_->isWriting())
        {
            channel_->enableWriting();
        }
    }
}

// channel的读事件发生时的处理函数
void TcpConnection::handleRead(Timestamp receiveTime)
{
    int savedError = 0;
    ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedError); // 从fd读到inputbuffer
    LOG_DEBUG("客户端#%s", inputBuffer_.output().c_str());
    if (n > 0)
    {
        messageCallback_(shared_from_this(), &inputBuffer_, receiveTime); // 从inputbuffer读取数据进行处理
    }
    else if (n == 0)
    {
        handleClose();
    }
    else
    {
        LOG_ERROR("TcpConnection::handleRead error: %d: %s", savedError, strerror(savedError));
        handleError();
    }
}

// channel的写事件发生的处理函数
void TcpConnection::handleWrite()
{
    if (channel_->isWriting())
    {
        int savedError = 0;
        ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedError); // 从发送缓冲区读取数据写入fd
        if (n > 0)
        {
            // outputBuffer_.retrieve(n);  我把它放在了writFd中了
            if (outputBuffer_.readableBytes() == 0)
            {
                channel_->disableWriting();

                if (writeCompleteCallback_)
                {
                    /**
                     * 假设subloop1发送完了数据，要在subloop1对应的线程执行writeCompleteCallback_，
                     * 而现在就是在其对应的线程，为什么不直接runInLoop呢？
                     * 我认为，如果直接runInLoop，则在没出handWrite函数的情况下就立马执行writeCompleteCallback_，
                     * queueInLoop呢，则是将函数放入队列，等出了handWrite函数后，再唤醒执行队列中的函数
                     */
                    loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
                }

                if (state_ == kDisconnecting) // 如果是kDisconnecting说明要关闭fd的写端
                {
                    shutdownInLoop();
                }
            }
        }
        else
        {
            LOG_ERROR("TcpConnection::handleWrite error: %d: %s", savedError, strerror(savedError));
        }
    }
    else
    {
        LOG_ERROR("TcpConnection fd=%d is down, no more writing", channel_->fd());
    }
}

// poller通知channel，Channel::closeCallbcak 实际就是这里的handleClose。构造函数注册的
void TcpConnection::handleClose()
{
    LOG_INFO("TcpConnection::handleClose fd=%d state=%s", channel_->fd(), stateToString());
    setState(kDisconnected);
    channel_->disableAll();
    // 没从Poller中的channels中移除

    TcpConnectionPtr connPtr(shared_from_this());
    connectionCallback_(connPtr); // TcpSever传入的

    // TcpSever绑定了TcpServer::removeConnection=>TcpServer::removeConnectionInLoop=>
    // TcpConnection::connectEstablished=>再次connectionCallback_ 结束
    connectDestroyed();
}

void TcpConnection::handleError()
{
    int err = 0;
    int optval;
    socklen_t optlen = static_cast<socklen_t>(sizeof optval);
    if (getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
    {
        // getsockopt函数出错
        err = errno;
    }
    else // 获取错误事件的错误码
    {
        err = optval;
    }
    LOG_ERROR("TcpConnection::handleError [%s] - SO_ERROR: %S", name_.c_str(), strerror(err));
    handleClose(); // 关闭连接
}

// 连接建立
void TcpConnection::connectEstablished()
{
    setState(kConnected);
    channel_->tie(shared_from_this());
    channel_->enableReading(); // 向poller注册关心channel的epollin事件

    connectionCallback_(shared_from_this()); // 连接建立完成，调用连接回调
}

// 连接销毁
void TcpConnection::connectDestroyed()
{
    if (state_ == kConnected)
    {
        setState(kDisconnected);
        channel_->disableAll();

        connectionCallback_(shared_from_this());
    }
    channel_->remove(); // cahnnel是不是没从Poller的channels中移除？ 仍然在epollpoller的events中，只不过不关心任何事件 TO PONDER
    socket_->Close();
    if (loop_->hasTimer(name_)) // 取消定时任务，
    {
        cancelInactiveReleaseInLoop();
    }
    closeCallback_(shared_from_this());
}

void TcpConnection::shutdown()
{
    if (state_ == kConnected)
    {
        setState(kDisconnecting);
        loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
    }
}

void TcpConnection::shutdownInLoop()
{
    if (!channel_->isWriting()) // 对写事件不关心了。说明outbuffer也没有数据要写了
    {
        socket_->shutdownWrite();
    }
}

// 删除定时任务
void TcpConnection::cancelInactiveReleaseInLoop()
{
    enableInactiveRelease_ = false;
    if (loop_->hasTimer(name_))
        return loop_->timerCancel(name_);
}

// 启动非活跃销毁，并定义了非活跃鉴定的时间
void TcpConnection::enableInactiveRelease(int sec)
{
    loop_->runInLoop(std::bind(&TcpConnection::enableInactiveReleaseInLoop, this, sec));
}

void TcpConnection::enableInactiveReleaseInLoop(int sec)
{
    enableInactiveRelease_ = true;
    // 如果定时销毁任务已存在，则刷新延迟
    if (loop_->hasTimer(name_))
    {
        return loop_->timerRefresh(name_);
    }
    // 如果不存在则添加
    loop_->timerAdd(name_, sec, std::bind(&TcpConnection::connectDestroyed, this));
    // loop_->timerAdd(name_, sec, std::bind(&TcpConnection::release, this));
}

const char *TcpConnection::stateToString() const
{
    switch (state_)
    {
    case kDisconnected:
        return "kDisconnected";
    case kConnecting:
        return "kConnecting";
    case kConnected:
        return "kConnected";
    case kDisconnecting:
        return "kDisconnecting";
    default:
        return "unknown state";
    }
}